#pragma once

#include "socket.hpp"
#include "HttpCol.hpp"
#include "log.hpp"
#include <pthread.h>
#include <memory>
#include <fstream>
#include <unordered_map>

extern Log lg;
const uint16_t defaultPort = 8080;
const size_t infoSize = 4096;

class threadInfo
{
public:
    threadInfo(int sockfd)
        : _sockfd(sockfd) {}

public:
    int _sockfd;
};

class HttpServer
{
public:
    HttpServer(uint16_t port = defaultPort)
        : _port(port) {}

    ~HttpServer() = default;

    void initSrcType()
    {
        _srcType.insert({".html", "text/html"});
        _srcType.insert({".jpg", "image/jpeg"});
    }

    void init()
    {
        if (_listen_sock.bind(_port) == -1)
        {
            lg(Fatal, "bind error, errno:%d, errstr:%s", errno, strerror(errno));
            exit(1);
        }
        if (_listen_sock.listen() == -1)
        {
            lg(Fatal, "listen error, errno:%d, errstr:%s", errno, strerror(errno));
            exit(2);
        }
        int opt = 1;
        setsockopt(_listen_sock.fd(), SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &opt, sizeof opt);
        initSrcType();
    }

    void start()
    {
        std::string srcIp;
        uint16_t srcPort;
        auto delete_fd = [](int *fd)
        {
            std::cout << "close done\n";
            close(*fd);
        };

        while (true)
        {
            int sockfd = _listen_sock.accept(&srcIp, &srcPort);
            if (sockfd < 0)
                continue;
            // read()
            pthread_t pid;
            threadInfo *ptd = new threadInfo(sockfd);
            pthread_create(&pid, nullptr, threadRun, ptd);
        }
    }

    static std::string getDate(const std::string &path)
    {
        // std::string line, text;
        std::ifstream fin(path, std::ios::binary);
        // 算出文件大小
        fin.seekg(0, std::ios_base::end);
        std::streamsize size = fin.tellg();
        fin.seekg(0, std::ios_base::beg);
        // while (std::getline(fin, line))
        //     text += line;
        std::string content;
        // 一种非常不规范的写法
        content.resize(size);
        fin.read((char *)content.c_str(), size);
        return content;
    }

    static int sendDate(const std::string &path, const int sockfd, const std::string &type)
    {
        std::string respon;
        std::string stateLine;
        std::string text = getDate(path);
        if (text.empty())
        {
            text = getDate(webRoot + errPage);
            stateLine = "HTTP/1.0 404 Not Found";
        }
        else
            stateLine = "HTTP/1.0 200 OK";

        // 302状态码实验
        // stateLine = "HTTP/1.0 302 Found";

        // connection 报头代表链接的属性，长链接表示为 keep-alive
        // 一张网页可能由大量资源组成，每张资源都必须要通过一个请求来获得
        // 每种资源的数据不同，如果要正确的解析，需要告诉资源的类型
        // http 协议是无状态的，利用cookie保存登录信息
        std::string header = "Content-Length: " + std::to_string(text.size());
        // cookie
        header += blankSym;
        header += std::string("Set-Cookie: ") + "usr=whb&pws=123";
        // data type
        header += blankSym;
        header += "content-Type: " + confirmType(type);
        // header += blankSym + "Location: https://www.qq.com" + blankSym;
        respon = stateLine + blankSym + header + blankSym;
        respon += blankSym + text;

        // std::cout << respon << std::endl;
        return send(sockfd, respon.c_str(), respon.size(), 0);
    }

    static std::string confirmType(std::string type)
    {
        // 默认设置成text/html
        if (_srcType.count(type))
            return _srcType[type];
        else
            return _srcType[".html"];
    }

    static void httpHandler(const int sockfd)
    {
        char inBuffer[infoSize];
        ssize_t n = recv(sockfd, inBuffer, infoSize - 1, 0);
        // std::cout << n << std::endl;
        if (n > 0)
        {
            inBuffer[n] = 0;
            std::cout << inBuffer;
            httpRequest hr;
            hr.deserializa(inBuffer);
            hr.parse();
            // hr.debugPrint();

            n = sendDate(hr._filePath, sockfd, hr._type);
        }
    }

    static void *threadRun(void *args)
    {
        // 分离之后主进程不需要等待
        pthread_detach(pthread_self());

        threadInfo *ptd = static_cast<threadInfo *>(args);
        int sockfd = ptd->_sockfd;
        httpHandler(sockfd);
        delete ptd;
        close(sockfd);
        return nullptr;
    }

private:
    tcp_socket _listen_sock;
    uint16_t _port;
    static std::unordered_map<std::string, std::string> _srcType;
};

std::unordered_map<std::string, std::string> HttpServer::_srcType;